package cn.yx.common.security.webflux.sms;

import cn.hutool.core.util.StrUtil;
import cn.yx.common.redis.service.RedisService;
import cn.yx.common.security.constants.SecurityRedisCacheConstant;
import cn.yx.common.security.pojo.JwtUserDetails;
import cn.yx.common.security.service.JwtUserDetailsService;
import cn.yx.common.security.webflux.constant.message.SmsCodeAuthenticationFilterMessageKey;
import cn.yx.common.security.webflux.vo.in.PhoneSmsCodeInVo;
import cn.yx.common.webflux.util.WebFluxUtil;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * <p>手机验证码登录过滤器，包含有手机号和手机验证码。依照UsernamePasswordAuthenticationFilter编写</p>
 *
 * @author Wgssmart
 */
@Component
@Order(-20)
public class SmsCodeAuthenticationFilter implements WebFilter {

    private final JwtUserDetailsService jwtUserDetailsService;

    private final RedisService redisService;

    public SmsCodeAuthenticationFilter(JwtUserDetailsService jwtUserDetailsService, RedisService redisService) {
        this.jwtUserDetailsService = jwtUserDetailsService;
        this.redisService = redisService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String phone = getPhone(request);
        String smsCode = getSmsCode(request);
        JwtUserDetails jwtUserDetails = (JwtUserDetails) jwtUserDetailsService.loadUserByUsername(phone);
        if (StrUtil.isEmpty(jwtUserDetails.getUsername())) {
            throw new UsernameNotFoundException(SmsCodeAuthenticationFilterMessageKey.PHONE_NOT_EXIST);
        }
        checkCode(phone, smsCode);
        return chain.filter(exchange);
    }

    /**
     * 校验验证码
     *
     * @param phone
     * @param smsCode
     */
    private void checkCode(String phone, String smsCode) {
        String smsCodeInRedis = (String) redisService.get(SecurityRedisCacheConstant.SMS_CODE_LOGIN_PREFIX + phone);
        if (StrUtil.isEmpty(smsCodeInRedis)) {
            // 短信验证码不存在
            throw new SmsCodeExpirationException(SmsCodeAuthenticationFilterMessageKey.SMS_CODE_NOT_EXIST);
        }
        if (!StrUtil.equals(smsCode, smsCodeInRedis)) {
            // 短信验证码错误
            throw new SmsCodeErrorException(SmsCodeAuthenticationFilterMessageKey.SMS_CODE_ERROR);
        }
    }

    /**
     * 从请求参数中获取手机号
     *
     * @param request
     * @return
     */
    private String getPhone(ServerHttpRequest request) {
        PhoneSmsCodeInVo phoneSmsCodeInVo = WebFluxUtil.getRequestData(request, PhoneSmsCodeInVo.class);
        String phone = phoneSmsCodeInVo.getPhone();
        if (StrUtil.isEmpty(phone)) {
            return "";
        }
        return phone.trim();
    }

    /**
     * 从请求参数中获取手机验证码
     *
     * @param request
     * @return
     */
    private String getSmsCode(ServerHttpRequest request) {
        Mono<PhoneSmsCodeInVo> phoneSmsCodeVoMono = Mono.from(request.getBody()).ofType(PhoneSmsCodeInVo.class);
        PhoneSmsCodeInVo phoneSmsCodeInVo = phoneSmsCodeVoMono.block();
        if (Objects.isNull(phoneSmsCodeInVo)) {
            return "";
        }
        String smsCode = phoneSmsCodeInVo.getSmsCode();
        if (StrUtil.isEmpty(smsCode)) {
            return "";
        }
        return smsCode;
    }

}